/**
 * 
 */
package experimenter;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.Formatter;
import java.util.Locale;

import utilities.FileComparer;

import coder.CoderID;
import coder.Compressor;
import coder.Decompressor;

/**
 * Klasa wykonujaca eksperymenty dla podanych parametrow: pliku + kodekow + rozmiaru bloku
 * 
 * @author Marcin Wachulski
 *
 */
public final class Experimenter 
{	
	/**
	 * Liczba wykonywanych pomiarow dla statystycznej poprawnosci eksperymentu
	 */
	private static final int MEASUREMENTS = 3;
	
	private File file;
	private CoderID[] coderIDs;
	private int blockSize;
	private String workingDir;
	
	// statystyka eksperymentu
	private double compTimeMillis = 0;
	private double decompTimeMillis = 0;
	private double bitRate = 0;
	
	public Experimenter(File file, CoderID[] coderIDs, int blockSize, String workingDir)
	{
		this.file = file;
		this.coderIDs = coderIDs;
		this.blockSize = blockSize;
		this.workingDir = workingDir;
	}
	
	/**
	 * Wykonuje eksperyment, mierzy czasy i srednia bitowa
	 * @return
	 * 		true - poprawna kompresja/dekompresja
	 * 		false - niepoprawna
	 */
	public boolean run() throws Exception
	{
		// przygotowanie struktu pomocniczych
		File originalFile = file;
		File compressedFile = new File(workingDir + File.separator + originalFile.getName() + ".koda");
		File decompressedFile = new File(workingDir + File.separator + originalFile.getName() + ".dekoda");
		
		FileInputStream input = null;
		FileOutputStream output = null;
		
		Compressor compressor = null;
		Decompressor decompressor = null;
		
		long[] compTimeMillisMeasurements = new long[MEASUREMENTS];
		long[] decompTimeMillisMeasurements = new long[MEASUREMENTS];
		double[] bitRateMeasurements = new double[MEASUREMENTS];
		
		compTimeMillis = decompTimeMillis = bitRate = 0.0;
		
		// wykonanie eksperymentu
		for (int measurementNo = 0; measurementNo < MEASUREMENTS; ++measurementNo)
		{
			input = new FileInputStream(originalFile);
			output = new FileOutputStream(compressedFile);
			compressor = new Compressor(input, output, blockSize);
			for (CoderID cID : coderIDs)
				compressor.addCoder(cID);
			
			// kompresja
			long startTime = System.currentTimeMillis();
			compressor.compress();
			long endTime = System.currentTimeMillis();
			input.close();
			output.close();
			compTimeMillisMeasurements[measurementNo] = endTime - startTime;
			bitRateMeasurements[measurementNo] = (double)compressedFile.length() / (double)originalFile.length();
			
			// dekompresja
			input = new FileInputStream(compressedFile);
			output = new FileOutputStream(decompressedFile);
			decompressor = new Decompressor(input, output);
			startTime = System.currentTimeMillis();
			decompressor.decompress();
			endTime = System.currentTimeMillis();
			input.close();
			output.close();
			decompTimeMillisMeasurements[measurementNo] = endTime - startTime;
			
			// weryfikacja poprawnosci
			if (!FileComparer.compareFiles(originalFile.getPath(), decompressedFile.getPath()))
				return false;
		}
		
		// usuniecie tymczasowych plikow
		compressedFile.deleteOnExit();
		decompressedFile.delete();
		
		// usrednianie wynikow koncowych
		for (int i = 0; i < MEASUREMENTS; ++i) {
			compTimeMillis += compTimeMillisMeasurements[i];
			decompTimeMillis += decompTimeMillisMeasurements[i];
			bitRate += bitRateMeasurements[i];			
		}
		compTimeMillis = (double)compTimeMillis / MEASUREMENTS;
		decompTimeMillis = (double)decompTimeMillis / MEASUREMENTS;
		bitRate = bitRate / MEASUREMENTS;
		
		return true;
	}
	
	// wyniki eksperymentow
	public double getCompressionTimeMillis() { return compTimeMillis; }
	public double getDecompressionTimeMillis() { return decompTimeMillis; }
	public double getBitRate() { return bitRate; }
	
	@Override
	public String toString()
	{
		StringBuilder sb = new StringBuilder(50)
			.append("[").append(file.getName()).append("->");
		for (CoderID cID : coderIDs)
			sb.append(cID).append("->");
		sb.append("*.koda; ").append(blockSize).append("B] =");
		Formatter fmt = new Formatter();
		fmt.format("%60s {cTime= %.4fs; dTime= %.4fs; bitrate= %.4f}", 
				sb.toString(), compTimeMillis / 1000, decompTimeMillis / 1000, bitRate);
		return fmt.toString();
	}
	
	public String toCSV() {
		StringBuilder sb = new StringBuilder(50);
		for (int i = 0; i < coderIDs.length - 1; ++i)
			sb.append(coderIDs[i]).append(" + ");
		sb.append(coderIDs[coderIDs.length - 1]).append(", ").append(file.getName() + ", ").append(blockSize / 1024).append(",");
		Formatter fmt = new Formatter(Locale.ENGLISH);
		fmt.format("%s %.0f, %.0f, %.4f", sb.toString(), compTimeMillis, decompTimeMillis, bitRate);
		return fmt.toString();
	}
}
